﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using InfiniTec;

namespace ApiScanner.Core
{
    public class FileSystemScanner : IScanner
    {
        private static readonly Dictionary<Type, Func<IFileSystemScannerModule>> _ModuleFactory = new Dictionary<Type, Func<IFileSystemScannerModule>>
                                                                                                       {
                                                                                  {typeof(ExchangeDllScannerTestConfigurationElement), () => new ExchangeDllScannerModule()},
                                                                                  {typeof(ExchangeDllDependencyScannerConfigurationElement), () => new ExchangeDllDependencyScannerModule()},
                                                                              };

        private FileSystemScannerTestConfigurationElement _Configuration;

        public void Initialize(TestConfigurationElement configuration)
        {
            _Configuration = (FileSystemScannerTestConfigurationElement) configuration;

            if (!Directory.Exists(_Configuration.RootDirectory)) throw new DirectoryNotFoundException(string.Format("The path {0} could not be found.", _Configuration.RootDirectory));

            if (_Configuration.Modules.OfType<ExchangeDllDependencyScannerConfigurationElement>().Any() && !DependsIsAvailable())
            {
                throw new InvalidOperationException("The dependency scanner module is activated, but Depends.exe is not available. Please install the Microsoft Windows Support Tools.");
            }
        }

        private static bool DependsIsAvailable()
        {
            var pathVariable = Environment.GetEnvironmentVariable("PATH");
            if (pathVariable.IsNullOrEmpty())
            {
                return false;
            }

            var searchPaths = pathVariable.Split(';');
            var result = from path in searchPaths
                         let cleanSearchPath = path.Trim('\"')
                         let filename = Path.Combine(cleanSearchPath, "depends.exe")
                         where File.Exists(filename)
                         select path;
            return result.Any();
        }

        public IEnumerable<Artifact> EnumerateArtifacts(Action<string> progressHandler)
        {
            var remainingDirectories = new List<string> { _Configuration.RootDirectory };

            while (remainingDirectories.Count > 0)
            {
                var directory = remainingDirectories[0];
                remainingDirectories.RemoveAt(0);

                remainingDirectories.AddRange(Directory.GetDirectories(directory));

                var modules = from moduleConfiguration in _Configuration.Modules
                              let moduleFactory = _ModuleFactory[moduleConfiguration.GetType()]
                              let module = moduleFactory()
                              select new {Module = module, Configuration = moduleConfiguration};

                var patterns = (from module in modules
                                from pattern in module.Module.FilePatterns
                                select pattern.ToLowerInvariant()).Distinct();

                var files = (from pattern in patterns
                             from file in Directory.GetFiles(directory, pattern)
                             select file);
                foreach (var file in files)
                {
                    progressHandler(string.Format("Processing file {0}", file));
                    foreach (var module in modules)
                    {
                        Artifact artifact;
                        try
                        {
                            artifact = module.Module.Execute(file, module.Configuration);
                        }
                        catch (Exception ex)
                        {
                            artifact = new ScannerError(string.Format("File {0}", file), ex);
                        }
                        if (artifact != null)
                        {
                            yield return artifact;
                        }
                    }
                }
            }
        }
    }
}